// ml278
// Copyright (C) 2008 Bjørn Øivind Bjørnsen, Aleksander Gjermundsen, Knut Andre Tidemann, Thomas Hansen
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

#ifndef __PLANETARYRENDERABLE_H_
#define __PLANETARYRENDERABLE_H_

#include <vector>

#include <OgreMaterialManager.h>
#include <OgreRenderable.h>
#include <OgreMovableObject.h>
#include <btBulletCollisionCommon.h>

namespace Ogre
{
	class PlanetaryTile;
	class PlanetaryOptions;
	class PlanetarySceneManager;

	/**
	* The visible part of a PlanetaryTile.
	* Initialises and loads vertex data into graphic card memory and handles showing and hiding of the tile.
	*/
	class PlanetaryRenderable : public Renderable, public MovableObject
	{
	public:
		PlanetaryRenderable(PlanetarySceneManager *mgr);
		~PlanetaryRenderable();

		/// Initialises the renderable
		/// @param zone The parent zone ID
		/// @param pageX The parent page X ID
		/// @param pageZ The parant page Z ID
		/// @param tileX The parent tile X ID that this renderable belongs to.
		/// @param tileZ The parent tile Z ID that this renderable belongs to.
		/// @param parent Pointer to the parent tile object.
		void init(unsigned int zone, unsigned int pageX, unsigned int pageZ, unsigned int tileX, unsigned int tileZ, PlanetaryTile *parent);
		/// Loads vertex data into memory based on information passed to init
		void load();
		/// Unloads data from memory
		void unload();
		/// Uninitialises buffers and other variables that have been initialised.
		void uninit();

		/// Set the material to use when rendering
		/// @param  mat The material to use
		void setMaterial(MaterialPtr mat);

		// MovableObject overridden methods
		/// Internal method overriden from MovableObject. Is called before each frame
		/// @param cam The current camera that will be used during the rendering
		virtual void _notifyCurrentCamera(Camera* cam);
		/// Internal method overriden from MovableObject. Called when the object is attached to a node.
		/// @param parent The node the joint got attached to
		virtual void _notifyAttached(Node* parent, bool isTagPoint = false);

		/// Internal method overriden from MovableObject. Adds this object's render operation to the rendering queue
		virtual void _updateRenderQueue(RenderQueue* queue);
		/// Internal method overriden from MovableObject. Sets this object's rendering operation parameters
		virtual void getRenderOperation(RenderOperation& rend);
		
		virtual void setVisible(bool visible);

		/// Internal method overriden from MovableObject. Gets the type name of this class
		virtual const String &getMovableType() const
		{
			return mType;
		}

		/// Internal method overriden from MovableObject. Gets the bounding box of this object
		virtual const AxisAlignedBox &getBoundingBox() const
		{
			return mBounds;
		}
		/// Internal method overriden from MovableObject. Gets the bounding radius of this object
		virtual Real getBoundingRadius() const
		{
			return mBoundingRadius;
		}


		// Renderable overridden methods
		/// Internal method overriden from Rnederable. Gets the current material
		virtual const MaterialPtr &getMaterial() const
		{
			return mMaterial;
		}

		/// Internal method overriden from Rnederable. Gets the current world transformation matrix
		virtual void getWorldTransforms(Matrix4 *xform) const;
		/// Internal method overriden from Rnederable. Gets the world orientation.
		virtual const Quaternion &getWorldOrientation() const;
		/// Internal method overriden from Rnederable. Gets the current world position
		virtual const Vector3 &getWorldPosition() const;
		/// Internal method overriden from Rnederable.
		virtual Real getSquaredViewDepth(const Camera *cam) const;
		/// Internal method overriden from Rnederable. Gets the list of lights
		virtual const LightList &getLights() const;



	protected:
		/// Functions to load the vertices into the buffer if the zone is ZONE_UP
		void loadUp(const VertexElement* poselem, uchar *pBuffer,unsigned int vertexSize);
		/// Functions to load the vertices into the buffer if the zone is ZONE_DOWN
		void loadDown(const VertexElement* poselem, uchar *pBuffer,unsigned int vertexSize);
		/// Functions to load the vertices into the buffer if the zone is ZONE_FRONT
		void loadFront(const VertexElement* poselem, uchar *pBuffer,unsigned int vertexSize);
		/// Functions to load the vertices into the buffer if the zone is ZONE_BACK
		void loadBack(const VertexElement* poselem, uchar *pBuffer,unsigned int vertexSize);
		/// Functions to load the vertices into the buffer if the zone is ZONE_LET
		void loadLeft(const VertexElement* poselem, uchar *pBuffer,unsigned int vertexSize);
		/// Functions to load the vertices into the buffer if the zone is ZONE_RIGHT
		void loadRight(const VertexElement* poselem, uchar *pBuffer,unsigned int vertexSize);

		/// Creates a delta buffer
		HardwareVertexBufferSharedPtr createDeltaBuffer(void) const;

		/// Scale of the renderable
		float mScale;

		/// Vertexbuffer
		VertexData *mVertices;
		/// Face Indexes
		IndexData *mIndices;

		/// MovableObject bounds
		AxisAlignedBox mBounds;

		/// MovableObject bounding radius
		Real mBoundingRadius;

		/// MovableObject type
		static String mType;

		/// Renderable material
		MaterialPtr mMaterial;

		/// Renderable center
		Vector3 mCenter;

		/// Whether light list need to re-calculate
		mutable bool mLightListDirty;
		/// Cached light list
		mutable LightList mLightList;

		/// Delta buffers used for LOD morphing
		HardwareVertexBufferSharedPtr* mDeltaBuffers;

		/// GPU parameters used for LOD morphing
		Vector4 mCustomGpuParameters;

		/// The morph factor between this and the next LOD level down
		Real mLODMorphFactor;

		/// Scene Manager object
		PlanetarySceneManager *mSceneMgr;

		/// Options object
		PlanetaryOptions *mOptions;

		/// Parent tile
		PlanetaryTile *mParent;

		// Location indexes
		/// Parent planet ID
		unsigned int mPlanet;
		/// Parent zone ID
		unsigned int mZone;
		/// Parent page X ID
		unsigned int mPageX;
		/// Parent page Z ID
		unsigned int mPageZ;
		/// Parent tile X ID
		unsigned int mTileX;
		/// Parent tile Z ID
		unsigned int mTileZ;

		/// Set to true to add vertex colours to tiles, making tiles very visible for debugging. Lighting needs to be disable for vertex colours to show.
		bool mDebugColours;

		/// True if the renderable is loaded and ready to be rendered
		bool mIsLoaded;
		/// True if the renderable is initialised and ready to be loaded
		bool mIsInit;
		
		// TODO: is it easier to make our own interface to VertexData and IndexData?
		/// bullet interface to the vertex and index data
		btTriangleIndexVertexArray *mIndexVertexArrays;
		/// Collision shape used for the ground
		btBvhTriangleMeshShape *mGroundShape;
		/// The vertices used for the collision shape
		btVector3 *mCollisionVertices;
		/// the indices used for the collision shape
		int *mCollisionIndices;
		/// static rigidbody for collisions
		btRigidBody *mBody;
		/// true if the rigidbody is added to the dynamics world
		bool mBodyIsInWorld;
	};
}

#endif
